package com.wontube.core.srv.schedule;

import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import com.wontube.core.srv.ServiceManager;
import com.wontube.core.srv.interval.IIntervalService;
import com.wontube.core.srv.interval.ITask;
import com.wontube.core.srv.time.ITimeListener;
import com.wontube.core.srv.time.ITimeService;


/**
 * @author yuechao
 * 
 */
public class ScheduleService implements IScheduleService, ITimeListener {
	private ScheduleTask scheduleTask;
	private Thread threadSheculaTask;
	private ExecutorService executorService;
	
	private static final ScheduleTaskHolder dummyTaskHolder = new ScheduleTaskHolder(null);

	// 检测线程
	private Thread checkThread;
	private CheckTask checkTask;

	@Override
	public void startup() {
		ITimeService s = ServiceManager.get(ITimeService.class);
		if (s != null) {
			// 如果计划任务已存在，使用原来的，防止重启时任务丢失
			if (this.checkTask == null) {
				this.checkTask = new CheckTask();
			} else {
				this.checkTask.clearPendingTask();
			}
			
			if (this.scheduleTask == null) {
				this.scheduleTask = new ScheduleTask(this.checkTask);
			} else {
				this.scheduleTask.clearPeedingTask();
			}

			this.executorService = Executors.newCachedThreadPool();		
			s.register(this); 

			this.threadSheculaTask = new Thread(this.scheduleTask);
			this.checkThread = new Thread(this.checkTask);
			this.threadSheculaTask.start();
			this.checkThread.start();
		}
	}

	@Override
	public void shutdown() {
		ITimeService s = ServiceManager.get(ITimeService.class);
		if (s != null) {
			s.unregister(this);
			this.checkTask.stop();
			this.scheduleTask.stop();
			this.executorService.shutdown();
			
			for (int i = 0; i < 10; i++) {
				if (!this.isAlive()) {
					break;
				}
				
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
				}
			}
		}
	}

	@Override
	public boolean isAlive() {
		return this.threadSheculaTask.isAlive() && this.checkThread.isAlive();
	}

	@Override
	public String name() {
		return "SheculeService";
	}

	@Override
	public Class<?>[] depends() {
		return new Class<?>[] { ITimeService.class, IIntervalService.class };
	}

	@Override
	public void setTime(Date time) {
		this.scheduleTask.check(time);
	}

	/*
	 * @see
	 * core.srv.schedule.IScheduleService#schedule(core.srv.schedule.IScheduleTask
	 * )
	 */
	@Override
	public void schedule(IScheduleTask task) {
		this.scheduleTask.scheduleTask(task);
	}

	/*
	 * @see core.srv.schedule.IScheduleService#removeSchedule(java.lang.Class)
	 */
	@Override
	public boolean removeTask(ITask task) {
		return this.scheduleTask.removeTask(task);
	}
	
	// nested types
	private class CheckTask implements Runnable {
		private BlockingQueue<ScheduleTaskHolder> checkQueue = new LinkedBlockingQueue<ScheduleTaskHolder>();

		public boolean addTask(ScheduleTaskHolder holder) {
			return this.checkQueue.offer(holder);
		}
		
		public void clearPendingTask() {
			this.checkQueue.clear();
		}
		
		public void stop() {
			this.checkQueue.clear();
			this.checkQueue.offer(ScheduleService.dummyTaskHolder);
		}
		
		@Override
		public void run() {
			while (true) {
				ScheduleTaskHolder holder = null;
				try {
					holder = this.checkQueue.take();
				} catch (InterruptedException e) {
				}
				
				if (holder == null) {
					continue;
				}

				if (holder == ScheduleService.dummyTaskHolder) {
					return;
				}

				if (holder.shouldExcute()) {
					if (holder.task.isAsync()) { // 异步，使用线程池执行
						TaskExecutor executor = new TaskExecutor(holder);
						ScheduleService.this.executorService.execute(executor);
					} else { // 放入队列，使用一个线程执行
						ScheduleService.this.scheduleTask.addTask(holder);
					}
				}
			}
		}
	}

	private static final class ScheduleTask implements Runnable {
		private final HashMap<ITask, ScheduleTaskHolder> taskMap = new HashMap<ITask, ScheduleTaskHolder>();

		// 任务队列
		private BlockingQueue<ScheduleTaskHolder> queue = new LinkedBlockingQueue<ScheduleTaskHolder>();

		private CheckTask checkTask;
		
		public ScheduleTask(CheckTask checkTask) {
			this.checkTask = checkTask;
		}
		
		public void clearPeedingTask() {
			this.queue.clear();
		}
		
		/**
		 * @param taskClazz
		 * @return
		 */
		public boolean removeTask(ITask task) {
			ScheduleTaskHolder holder = this.taskMap.remove(task);
			return holder != null;
		}
		
		/**
		 * 添加一个任务
		 * 
		 * @param holder
		 */
		public void addTask(ScheduleTaskHolder holder) {
			this.queue.offer(holder);
		}

		/**
		 * 停止，可能需要等待
		 * 
		 */
		public void stop() {
			this.addTask(ScheduleService.dummyTaskHolder);
		}

		public void check(Date time) {
			ScheduleTaskHolder[] list = this.taskMap.values().toArray(new ScheduleTaskHolder[0]);
			for (int i = 0; i < list.length; i++) {
				// 获取拷贝，冻结时间
				ScheduleTaskHolder holder = list[i].copy();
				holder.setTime(time);
				this.checkTask.addTask(holder);
			}
		}

		public void scheduleTask(IScheduleTask task) {
			ScheduleTaskHolder holder = new ScheduleTaskHolder(task);
			this.taskMap.put(task, holder);
		}

		/*
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run() {
			while (true) {
				ScheduleTaskHolder holder = null;
				try {
					holder = this.queue.take();
				} catch (InterruptedException e) {
				}
				
				if (holder == null) {
					continue;
				}
				
				if (holder == ScheduleService.dummyTaskHolder) {
					this.addTask(ScheduleService.dummyTaskHolder);
					return;
				}

				holder.executeTask();
			}
		}
	}
	
	private static class TaskExecutor implements Runnable {
		private ScheduleTaskHolder holder;
		
		public TaskExecutor(ScheduleTaskHolder holder) {
			this.holder = holder;
		}

		/* 
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run() {
			this.holder.executeTask();
		}
	}
	
	private static class ScheduleTaskHolder {
		private IScheduleTask task;
		private Date time;

		public ScheduleTaskHolder(IScheduleTask task) {
			this.task = task;
		}

		public boolean shouldExcute() {
			return this.task.shouldExcute(this.time);
		}

		public void setTime(Date value) {
			this.time = value;
		}

		public void executeTask() {
			if (this.task != null) {
				this.task.execute(this.time);
			}
		}

		public ScheduleTaskHolder copy() {
			ScheduleTaskHolder holder = new ScheduleTaskHolder(this.task);
			holder.setTime(this.time);
			
			return holder;
		}
	}
}
